#pragma once
#include "requestor.hpp"
#include <unordered_map>

//注册端和发现端->不在同一个主机上->分离的
//服务提供者,需要服务注册,连接注册中心,进行服务注册
//服务发现者,需要服务发现,连接注册中心,进行服务发现  需要服务上线/下线通知请求的处理(向dispatcher提供一个请求处理的回调函数)
  //需要将获取到的能够提供指定服务的主机信息管理起来 hash<method,vector<host>>->长连接(一次发现,多次使用):短连接就不需要,每次需要请求就先进行注册中心进行发现

namespace rpc
{
    namespace client
    {
        //服务提供者
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr & requestor):_requestor(requestor){}
            //服务注册->连接注册中心的链接:方法:主机
            bool registryMethod(const BaseConnection::ptr & conn,const std::string & method,const Address & host)
            {
                auto msg_req = MessageFactory::create<ServiceRequest>();//服务请求
                msg_req->setId(UUID::uuid());
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setMethod(method);
                msg_req->setHost(host);
                msg_req->setOptype(ServiceOptype::SERVICE_REGISTRY);//服务注册
                BaseMessage::ptr msg_rsp;

                bool ret = _requestor->send(conn,msg_req,msg_rsp);//同步操作
                if(ret == false)
                {
                    LOG(ERROR,"%s 服务注册失败",method.c_str());
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);//基类转换为子类
                if(service_rsp.get() == nullptr)
                {
                    LOG(ERROR,"响应类型向下转型失败");
                    return false;
                }
                if(service_rsp->rcode() != RCode::RCODE_OK)
                {
                    LOG(ERROR,"服务注册失败,原因: %s",errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                return true;//注册成功
            }
        private:
            Requestor::ptr _requestor; //通过requestor进行发送消息
        };

        //对Host进行封装
        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost():_idx(0){}
            MethodHost(const std::vector<Address> & hosts):
            _hosts(hosts),_idx(0)
            {}

            //添加主机信息->中途收到服务上线请求后进行调用
            void appendHost(const Address & host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }

            //删除主机
            void removeHost(const Address & host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for(auto it = _hosts.begin();it != _hosts.end();++it)
                {
                    if(*it == host)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }

            //选择一个主机进行处理->频率较高
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos = _idx++%_hosts.size();
                return _hosts[pos];
            }

            //判断请求服务的服务者是否为空
            bool empty()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _hosts.empty();
            }
        private:
            std::mutex _mutex;
            size_t _idx;//索引
            std::vector<Address> _hosts;//对应的主机->选择vector:unordered_set不能根据下标查找元素
        };

        //服务发现者->RR轮转请求对应服务发现到的主机->需要一个索引->对Host进行封装
        class Discoverer
        {
        public:
            using OfflineCallback = std::function<void(const Address&)>;
            using ptr = std::shared_ptr<Discoverer>;
            Discoverer(const Requestor::ptr & requestor,const OfflineCallback & cb)
            :_requestor(requestor),_offline_callback(cb)
            {}

            //进行服务发现->返回一个主机
            bool serviceDiscovery(const BaseConnection::ptr & conn,const std::string & method,Address & host)
            {
                //保管的提供者信息存在,直接返回地址
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _method_hosts.find(method);
                    if(it != _method_hosts.end())
                    {
                        if(it->second->empty() == false)
                        {
                            host = it->second->chooseHost();
                            return true;
                        }
                    }
                }
                //当前服务提供者为空
                auto msg_req = MessageFactory::create<ServiceRequest>();
                msg_req->setId(UUID::uuid());
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setMethod(method);
                msg_req->setOptype(ServiceOptype::SERVICE_DISCOVERY);//服务发现
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->send(conn,msg_req,msg_rsp);//发送请求->同步请求
                if(ret == false)
                {
                    LOG(ERROR,"服务发现失败");
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if(!service_rsp)
                {
                    LOG(ERROR,"服务发现失败,响应类型转换失败");
                    return false;
                }
                if(service_rsp->rcode() != RCode::RCODE_OK)
                {
                    LOG(ERROR,"服务发现失败 %s",errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                //请求成功->当前没有对应的服务提供主机
                std::unique_lock<std::mutex> lock(_mutex);
                auto method_host = std::make_shared<MethodHost>(service_rsp->hosts());
                if(method_host->empty())
                {
                    LOG(ERROR,"%s 服务发现失败,没有能够提供服务的主机",method.c_str());
                    return false;
                }
                host=method_host->chooseHost();
                _method_hosts[method] = method_host;
                return true;
            }

            //提供给dispatcher进行服务上线下线请求处理的回调函数
            void onServiceRequest(const BaseConnection::ptr & conn,const ServiceRequest::ptr & msg )
            {
                //判断是上线还是下线
                auto optype = msg->optype();
                std::string method = msg->method();
                std::unique_lock<std::mutex> lock(_mutex);
                if(optype == ServiceOptype::SERVICE_ONLINE) //上线请求
                {
                    auto it = _method_hosts.find(method);
                    if(it == _method_hosts.end())//没有找到->但是以前发现过
                    {
                        auto method_host = std::make_shared<MethodHost>();
                        method_host->appendHost(msg->host());
                        _method_hosts[method] = method_host;
                    }
                    else 
                    {
                        it->second->appendHost(msg->host());
                    }
                }
                else if(optype == ServiceOptype::SERVICE_OFFLINE) //下线请求
                {
                    auto it = _method_hosts.find(method);
                    if(it == _method_hosts.end())
                    {
                        return;
                    }
                    it->second->removeHost(msg->host());//主机提供者为空,不需要删除这个键值对
                    _offline_callback(msg->host());//服务下线进行调用删除连接池的客户端
                }
            }
        private:
            OfflineCallback _offline_callback;//服务下线进行调用删除客户端
            std::mutex _mutex;
            std::unordered_map<std::string,MethodHost::ptr> _method_hosts;//方法和对应的提供者
            Requestor::ptr _requestor;//发送请求
        };
    }
}